﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace Permission.Repository
{
    public interface IOperate<TEntity>
    {

        #region 属性

        /// <summary>
        /// 获取 当前实体的查询数据集
        /// </summary>
        IQueryable<TEntity> Entities { get; }

        #endregion

        void Dispose();

        #region 新 增

        /// <summary>
        /// 新增一条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        int Insert(TEntity entities);

        /// <summary>
        /// 添加一条记录并返回实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        TEntity InsertAndLoadTEntity(TEntity entity);

        /// <summary>
        /// 新增多条记录
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        int InsertRange(IEnumerable<TEntity> entities);

        #endregion

        #region 修 改

        /// <summary>
        /// 修改整个实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        int Update(TEntity entity);

        /// <summary>
        /// 根据表达式参数，按需修改实体
        /// </summary>
        /// <param name="propertyExpression"></param>
        /// <returns></returns>
        int Update(Expression<Func<TEntity, TEntity>> propertyExpression);


        /// <summary>
        /// 根据条件修改（***扩展方法***）
        /// </summary>
        /// <param name="filterExpression"></param>
        /// <param name="updateExpression"></param>
        /// <returns></returns>
        int Update(Expression<Func<TEntity, bool>> filterExpression, Expression<Func<TEntity, TEntity>> updateExpression);

        #endregion

        #region 删 除

        /// <summary>
        /// 根据条件删除数据（***扩展方法***）
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        int Delete(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// 删除实体,只需要主键
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        int Delete(TEntity entity);

        /// <summary>
        /// 删除多条记录
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        int DeleteRange(IEnumerable<TEntity> entities);

        #endregion


        #region 执行语句

        int ExecuteSqlCommand(string sql, object[] paras);

        List<TEntity> SqlQuery(string queryCmdStr, object[] paras);

        #endregion

    }

    //---------------------------------------------以下为实现接口类--------------------------------------
    public class EfOperate<TEntity> : IOperate<TEntity> where TEntity : class, new()
    {
        private readonly string _dbContextString;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContext"></param>
        public EfOperate(string dbContext)
        {
            _dbContextString = dbContext;
        }

        private DbContext _dbContext;

        public DbContext DbContexts
        {
            get { return new Permission.DataModel.PermissionEntities(); }
        }



        /// <summary>
        /// 获得数据库链接的上下文
        /// </summary>
        public DbContext GetContext
        {
            get
            {
                //这里说明一下，为什么Dispose()后，又要设置为Null，因为Dispose后，实例并没有变为Null，这样再下一次使用时，无法又要重新实例化。
                if (_dbContext == null)
                {
                    if (string.IsNullOrEmpty(_dbContextString))
                        throw new Exception("传入的DbConnect为Null");
                    //_dbContext= DbContexts.GetDbContext(_dbContextString);
                }
                return _dbContext;
            }
        }

        public void Dispose()
        {
            if (_dbContext == null) return;
            _dbContext.Dispose();
            //这里说明一下，为什么Dispose()后，又要设置为Null，因为Dispose后，实例并没有变为Null，为了让实例不再重新实例化特设置为Null。结合获取_dbContext实例看更直观些。
            _dbContext = null;
        }

        #region 集 合

        public IQueryable<TEntity> Entities
        {
            get
            {
                return GetContext.Set<TEntity>();//此时EF还没有去真正的执行Sql语句，当ToList()的时候才会把翻译成sql语句（也即select）去操作数据库
            }
        }

        #endregion

        #region 添 加

        public int Insert(TEntity entity)
        {
            GetContext.Set<TEntity>().Add(entity);
            int rtnNumber = GetContext.SaveChanges();
            Dispose();
            return rtnNumber;
        }

        public TEntity InsertAndLoadTEntity(TEntity entity)
        {
            EntityState state = GetContext.Entry(entity).State;
            if (state == EntityState.Detached)
            {
                GetContext.Entry(entity).State = EntityState.Added;
            }
            GetContext.SaveChanges();
            Dispose();

            return entity;
        }

        /// <summary>
        /// 添加多条记录
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int InsertRange(IEnumerable<TEntity> entities)
        {
            try
            {
                GetContext.Set<TEntity>().AddRange(entities);
                int rtnNumber = GetContext.SaveChanges();
                // _dbContext.Dispose()后，实例并没有变为Null，
                Dispose();
                return rtnNumber;
            }
            catch (Exception error)
            {
                throw new Exception(error.Message);
            }
        }

        #endregion

        #region 修 改
        /// <summary>
        /// 缺点：必须在从数据库加载后的基础上修改，而不能对一个new的对象进行修改（即便设置了主键）
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public int Update(TEntity entity)
        {
            var entry = GetContext.Entry(entity);
            if (entry.State == EntityState.Detached)
            {
                GetContext.Set<TEntity>().Attach(entity);
                entry.State = EntityState.Modified;
            }
            int rtnNumber = GetContext.SaveChanges();
            Dispose();
            return rtnNumber;
        }

        /// <summary>
        /// 优点：不需要从数据库中再加载一次，可对部分字段修改；
        /// 缺点：必须设置此数据的主键值，否则系统报错
        /// 例子：UpdateUserPart(o=>new Sys_User{Id=user.Id, Code = user.Code,PreIpAddress = user.PreIpAddress});
        /// </summary>
        /// <param name="propertyExpression"></param>
        /// <returns></returns>
        public int Update(Expression<Func<TEntity, TEntity>> propertyExpression)
        {
            try
            {
                var memberInitExpression = propertyExpression.Body as MemberInitExpression;
                var entity = EntityHelper.CopyPropertyValue(propertyExpression);
                DbEntityEntry<TEntity> entry = GetContext.Entry(entity);
                entry.State = EntityState.Unchanged;
                if (memberInitExpression != null)
                    foreach (var memberInfo in memberInitExpression.Bindings)
                    {
                        string propertyName = memberInfo.Member.Name;
                        entry.Property(propertyName).IsModified = true;
                    }
                GetContext.Configuration.ValidateOnSaveEnabled = false;
            }
            finally
            {
                GetContext.Configuration.AutoDetectChangesEnabled = true;
            }
            int rtnNumber = GetContext.SaveChanges();
            Dispose();
            return rtnNumber;
        }

        /// <summary>
        /// 修改-扩展方法
        /// 可根据条件修改多条记录的部分字段
        /// _provider.Update(o =>o.Password.EndsWith("DDD"),o=>new Sys_User{Code = "RDTS"});
        /// 表示满足Password字段以DDD结尾的记录，修改其字段Code的值为传递过来的RDTS
        /// </summary>
        /// <param name="filterExpression">要修改的记录的条件表达式，若直接为true，则表示全部满足要修改的记录</param>
        /// <param name="updateExpression">要修改的部分字段表达式</param>
        /// <returns></returns>
        public int Update(Expression<Func<TEntity, bool>> filterExpression, Expression<Func<TEntity, TEntity>> updateExpression)
        {
            int rtnNumber = GetContext.Set<TEntity>().Where(filterExpression).Update(updateExpression);
            Dispose();
            return rtnNumber;
        }

        #endregion

        #region 删 除

        public int Delete(TEntity entity)
        {
            #region 方法1 已测试 直接删除，优点：使用任何类，把记录直接添加到上下文中，当Remove时会把状态自动改为Deleted
#if ceshi

DbContextTransaction transaction = null;
try
{
using (var context = GetContext)
{
using (transaction = context.Database.BeginTransaction())
{
//if (_module.Set<TEntity>().Any(o => o.Id == entity.Id))
//{
context.Set<TEntity>().Attach(entity);
context.Set<TEntity>().Remove(entity);
int rtnNumber = context.SaveChanges();
transaction.Commit();
return rtnNumber;
//}
//return 0;
}
}
}
catch (Exception error)
{
if (transaction != null)
transaction.Rollback();
throw new Exception(error.Message);
}

#endif

            #endregion

            GetContext.Configuration.AutoDetectChangesEnabled = false;
            DbEntityEntry<TEntity> entry = GetContext.Entry(entity);
            GetContext.Configuration.ValidateOnSaveEnabled = false;
            entry.State = EntityState.Deleted;
            GetContext.Configuration.AutoDetectChangesEnabled = true;
            int rtnNumber = GetContext.SaveChanges();
            Dispose();
            return rtnNumber;
        }

        /// <summary>
        /// 删除-扩展方法
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public int Delete(Expression<Func<TEntity, bool>> predicate)
        {
            int rtnNumber = GetContext.Set<TEntity>().Where(predicate).Delete();
            Dispose();
            return rtnNumber;
        }

        /// <summary>
        /// 删除多条记录
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int DeleteRange(IEnumerable<TEntity> entities)
        {
            try
            {
                foreach (var entity in entities)
                {
                    var entry = GetContext.Entry(entity);
                    if (entry.State == EntityState.Detached)
                    {
                        entry.State = EntityState.Deleted;
                    }
                }
                int rtnNumber = GetContext.SaveChanges();
                Dispose();
                return rtnNumber;
            }
            catch (Exception error)
            {
                throw new Exception(error.Message);
            }
        }

        #endregion

        #region 执行语句

        public int ExecuteSqlCommand(string sql, object[] paras)
        {
            DbContextTransaction transaction = null;
            try
            {
                using (transaction = GetContext.Database.BeginTransaction())
                {
                    int rtnNUmber = paras == null
                    ? GetContext.Database.ExecuteSqlCommand(sql)
                    : GetContext.Database.ExecuteSqlCommand(sql, paras);
                    GetContext.SaveChanges();
                    transaction.Commit();
                    Dispose();
                    return rtnNUmber;
                }
            }
            catch (Exception error)
            {
                if (transaction != null)
                    transaction.Rollback();
                throw new Exception("数据执行时发生异常:" + error.Message);
            }
        }

        public List<TEntity> SqlQuery(string queryCmdStr, object[] paras)
        {
            try
            {
                var lists = paras == null
                ? GetContext.Database.SqlQuery<TEntity>(queryCmdStr).ToList()
                : GetContext.Database.SqlQuery<TEntity>(queryCmdStr, paras).ToList();
                Dispose();
                return lists;
            }
            catch (Exception error)
            {
                throw new Exception(error.Message);
            }
        }

        #endregion
    }
}
